package project.layer.agents;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import framework.events.EventBroker;
import framework.instructions.InstructionManager;
import project.data.PickUpIntention;
import project.events.packageEvents.PackagePickedEvent;
import project.layer.ant.AntLayer;
import project.layer.ant.FeasibilityAnt;
import project.layer.ant.SearchingExplorationAnt;
import project.layer.devices.AStarRouter;
import project.layer.devices.Trajectory;
import project.layer.physical.Crossroad;
import project.layer.physical.Package;
import project.layer.physical.PackagePoint;
import project.layer.physical.Path;
import project.layer.physical.PickUpPoint;
import project.testenvironment.TestingParameters;



public class PickUpPointAgent extends PackagePointAgent implements ReportReceiver{
	private AStarRouter router;
	private Path pathToPackage = null;
	private int feasCyclesLeftTillHopCountIncrease = getNumberOfCyclesTillRangeIncrease();
	private int currentFeasibilityAntInitialPheromoneCount =  getFeasibilityAntStandardInitialPheromoneCount();
	private HashMap<ReportReceiver, Path> shortestPathForReportReceiver = new HashMap<ReportReceiver, Path>();
	private HashMap<ReportReceiver, ReportReceiver> earlierReceiverForReportReceiver = new HashMap<ReportReceiver, ReportReceiver>();
	
	
	
	public PickUpPointAgent(int packageID){
		super(packageID);
		this.router = new AStarRouter.DistanceBasedAStar();
		
	}

	public void reportPathTo(Path path, List<Integer> destinationsOfPreviousPndSequences,ReportReceiver sendingAgent, ReportReceiver earlierReceiver,TruckAgent originatingAgent, int sequencesLeft, long travelledDistance){
		if(sendingAgent.isTruckAgent()){	
			TruckAgent truckSendingAgent = (TruckAgent) sendingAgent;
			if(!truckSendingAgent.getTruck().isOnConnector() || !truckSendingAgent.getTruck().getConnectorPosition().getConnector().equals(path.getFirstCrossroadOfPath())){
				shortestPathForReportReceiver.remove(sendingAgent);
				earlierReceiverForReportReceiver.remove(sendingAgent);
			}
		}
		if(shortestPathForReportReceiver.keySet().contains(sendingAgent)){
			if(shortestPathForReportReceiver.get(sendingAgent).getTotalDistance() > travelledDistance){
				shortestPathForReportReceiver.put(sendingAgent, path);
			}
		}
		else{
			shortestPathForReportReceiver.put(sendingAgent, path);
			earlierReceiverForReportReceiver.put(sendingAgent, earlierReceiver);
			Path pathOfPreviousPndSequences = new Path(new LinkedList<Crossroad>(),destinationsOfPreviousPndSequences);
			pathOfPreviousPndSequences.addDestinations(shortestPathForReportReceiver.get(sendingAgent).getDestinations());
			SearchingExplorationAnt newAnt = new SearchingExplorationAnt(originatingAgent,  this, sendingAgent,getPickUpPoint().getPdpPackage().getDestination(), TestingParameters.EXPLORATION_ANT_INITIAL_MAX_HOPS, pathOfPreviousPndSequences, sequencesLeft);
		}
		
	}
	public void reportPath(Path path, ReportReceiver earlierSendingAgent){
		Path fullPath = new Path();
		fullPath.add(shortestPathForReportReceiver.get(earlierSendingAgent).getClone());
		path.removeFirstCrossroadFromPath();
		fullPath.add(path);
		earlierSendingAgent.reportPath(fullPath.getClone(), earlierReceiverForReportReceiver.get(earlierSendingAgent));
	}
	
	
	
	public boolean isTruckAgent(){
		return false;
	}
	
	@Override
	public boolean isPickUpPointAgent(){
		return true;
	}
	private Path calculatePathToPackage(){
		PickUpPoint point = (PickUpPoint) this.getDevice().getPhysicalEntity();
		if(point.hasPackage()){
			Crossroad cr = point.getConnector();
			System.out.println("A Star from: "+ point.getConnector().getId());
			System.out.println("A Star to: "+ point.getPdpPackage().getDestination().getId());
			Trajectory tr = router.calculateTrajectory(point.getConnector(), point.getPdpPackage().getDestination());
			Path result =  new Path((LinkedList<Crossroad>)tr.getTrajectory());
			//de A* router geeft blijkbaar al een pad terug zonder het startpunt
//			result.removeFirstCrossroadFromPath();
			result.addToDestinations(point.getPdpPackage().getDestination().getDropOffPoint().getCorrespondingAgent().getId());
			
			return result;
		}
		return null;

	}
	
	public boolean hasPackage(){
		return getPickUpPoint().hasPackage();
	}
	
	public boolean acceptOrRejectIntention(PickUpIntention intention){
		resetFeasibilityCyclesLeftTillRangeIncrease();
		resetFeasibilityRange();
		if(!hasPackage()){
			return false;
		}
		if(!hasIntention()){
			setCurrentIntention(intention);
			return true;
		}
		else{
			if(intention.getIntendingAgentID() == getCurrentIntention().getIntendingAgentID()){
				AntLayer.getInstance().unregisterEvaporableData(getCurrentIntention());
				setCurrentIntention(intention);
				return true;
			}
			else{
				if(intention.getETA() < (getIntentionRejectionMargin() * (double)getCurrentIntention().getETA()) ){
					//New intention is considered significantly better than old intention.
					AntLayer.getInstance().unregisterEvaporableData(getCurrentIntention());
					setCurrentIntention(intention);
					return true;
				}
				else{
					return false;
				}
			}
			
		}
	}
	
	
	
	public static double getIntentionRejectionMargin() {
		return TestingParameters.INTENTION_REJECTION_MARGIN;
	}

	@Override
	public PickUpIntention getCurrentIntention(){
		return (PickUpIntention) this.currentIntention;
	}
	
	private PickUpPoint getPickUpPoint(){
		return ((PickUpPoint) getDevice().getPhysicalEntity());
	}

	@Override
	protected void sendFeasibilityAntIfNecessary() {
		if(hasPackage()){
			sendFeasibilityAnt();
		}
		
	}

	public Package pickUpPackage() {
		Package result = getPickUpPoint().getPdpPackage();
		getPickUpPoint().releasePackage();
		EventBroker.getEventBroker().notifyAll(new PackagePickedEvent(result.getId(), result.getDestination().getPosition()));
		return result;
	}

	public Path getPathToPackage() {
		if(this.pathToPackage == null){
			this.pathToPackage = calculatePathToPackage();
		}
		return pathToPackage.getClone();
	}
	
	private void resetFeasibilityCyclesLeftTillRangeIncrease(){
		feasCyclesLeftTillHopCountIncrease = getNumberOfCyclesTillRangeIncrease();
	}
	
	public static final int getNumberOfCyclesTillRangeIncrease(){
		return TestingParameters.NUMBER_OF_FEASIBILITY_CYCLES_BEFORE_RANGE_INCREASE;
	}
	
	public static final int getFeasibilityAntStandardInitialPheromoneCount(){
		return TestingParameters.FEASIBILITY_ANT_STANDARD_INITIAL_PHEROMONE_COUNT;
	}
	
	public static final int getFeasibilityAntPheromoneCountIncrease(){
		return TestingParameters.FEASIBILITY_ANT_PHEROMONE_INCREASE;
	}
	
	public static final int getFeasibilityAntMaxInitialPheromoneCount(){
		return TestingParameters.FEASIBILITY_ANT_MAX_INITIAL_PHEROMONE_COUNT;
	}
	
	private void increaseFeasibilityRange(){
		this.currentFeasibilityAntInitialPheromoneCount +=  getFeasibilityAntPheromoneCountIncrease();
		if(this.currentFeasibilityAntInitialPheromoneCount > getFeasibilityAntMaxInitialPheromoneCount()){
			this.currentFeasibilityAntInitialPheromoneCount = getFeasibilityAntMaxInitialPheromoneCount();
		}
	}
	
	private void resetFeasibilityRange(){
		this.currentFeasibilityAntInitialPheromoneCount = getFeasibilityAntStandardInitialPheromoneCount();
	}
	
	
	protected void sendFeasibilityAnt() {
		FeasibilityAnt feasibilityAnt = new FeasibilityAnt(Integer.MAX_VALUE, this, ((PackagePoint)this.getDevice().getPhysicalEntity()).getConnector(), getCurrentFeasibilityAntInitialPheromoneCount());
		this.feasCyclesLeftTillHopCountIncrease--;
		if(this.feasCyclesLeftTillHopCountIncrease == 0){
			feasCyclesLeftTillHopCountIncrease = getNumberOfCyclesTillRangeIncrease();
			increaseFeasibilityRange();
			
		}
	}

	private int getCurrentFeasibilityAntInitialPheromoneCount() {
		return currentFeasibilityAntInitialPheromoneCount;
	}
}
